[id].vue 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264
  1. // 商品详情页面
  2. <template>
  3. <div>
  4. <StoreHeaderCat ref="headercat" @updateFllow="updateFllow" />
  5. <div class="sld_goods_detail">
  6. <!-- 内容区顶部固定 start -->
  7. <div class="contain_con" v-if="containCon">
  8. <div class="contain_content flex_row_center_center">
  9. <div class="contain_con_left flex_row_start_center">
  10. <span
  11. class="store_type"
  12. v-if="goodsDetail.data.storeInf.isOwnStore == 1"
  13. >{{ L["自营"] }}</span
  14. >
  15. <span class="store_title">{{
  16. goodsDetail.data.storeInf.storeName
  17. }}</span>
  18. </div>
  19. <div class="contain_con_right">
  20. <div class="goods_description_title flex_row_between_center">
  21. <div class="description_title_left flex_row_start_center">
  22. <span
  23. class="cursor_pointer"
  24. :class="{ description_active: goodsDesctionType == 'detail' }"
  25. @click="goodsDescType('detail')"
  26. >{{ L["商品详情"] }}</span
  27. >
  28. <span
  29. class="cursor_pointer"
  30. :class="{
  31. description_active: goodsDesctionType == 'evaluate',
  32. }"
  33. @click="goodsDescType('evaluate')"
  34. >{{ L["商品说明"] }}</span
  35. >
  36. <span
  37. class="cursor_pointer"
  38. :class="{
  39. description_active: goodsDesctionType == 'service',
  40. }"
  41. @click="goodsDescType('service')"
  42. >{{ L["商品服务"] }}</span
  43. >
  44. <span
  45. class="cursor_pointer"
  46. :class="{
  47. description_active: goodsDesctionType == 'salestore',
  48. }"
  49. @click="goodsDescType('salestore')"
  50. >{{ L["店铺热销"] }}</span
  51. >
  52. </div>
  53. </div>
  54. </div>
  55. </div>
  56. </div>
  57. <!-- 内容区顶部固定 end -->
  58. <div class="goods_detail_content self_background" v-if="goodsDetail.data">
  59. <!-- 商品所属分类 ,联系客服,关注店铺 start-->
  60. <div class="goods_about_con">
  61. <div class="goods_about flex_row_between_center">
  62. <div class="goods_classify">
  63. <span
  64. v-for="(item, index) in goodsDetail.data.categoryPath"
  65. :key="index"
  66. >
  67. <nuxt-link
  68. :to="`/goods/list/${calcProductName(item)}_v-${ goodsDetail.data.categoryIdPath[index] }_gid-${index + 1}${index > 0? '_pid-' + goodsDetail.data.categoryIdPath[index - 1]: '_pid-0'
  69. }`"
  70. target="_blank"
  71. >
  72. {{ item }}</nuxt-link
  73. >
  74. <i> > </i>
  75. </span>
  76. <span style="color: #333333">{{
  77. goodsDetail.data.goodsName
  78. }}</span>
  79. </div>
  80. <div class="goods_about_right flex_row_between_center">
  81. <router-link
  82. target="_blank"
  83. :to="'/store/'+ calcProductName(goodsDetail.data.storeInf.storeName) +'_'+ goodsDetail.data.storeInf.storeId"
  84. class="goods_about_store flex_row_between_center"
  85. v-if="goodsDetail.data.storeInf"
  86. >
  87. <span>{{ goodsDetail.data.storeInf.storeName }}</span>
  88. <span
  89. v-if="
  90. goodsDetail.data.storeInf &&
  91. goodsDetail.data.storeInf.isOwnStore == '1'
  92. "
  93. >{{ L["自营"] }}</span
  94. >
  95. </router-link>
  96. <div
  97. class="contact_service focus_store"
  98. @click="focusStore"
  99. v-if="goodsDetail.data.storeInf"
  100. >
  101. <img
  102. src="/goods/collection.png"
  103. alt=""
  104. v-show="goodsDetail.data.storeInf.isFollowStore == true"
  105. />
  106. <img
  107. src="/goods/no_collection.png"
  108. alt=""
  109. v-show="goodsDetail.data.storeInf.isFollowStore == false"
  110. />
  111. <span>
  112. {{
  113. goodsDetail.data.storeInf.isFollowStore
  114. ? L["取消关注"]
  115. : L["关注店铺"]
  116. }}
  117. </span>
  118. </div>
  119. </div>
  120. </div>
  121. </div>
  122. <!-- 商品所属分类 ,联系客服,关注店铺 end-->
  123. <!-- 商品主要信息 start -->
  124. <div class="main_con">
  125. <!-- 商品相关 start -->
  126. <div class="goods_des">
  127. <!-- 商品图片列表 start -->
  128. <div class="goods_des_left">
  129. <!-- 商品放大镜效果 start-->
  130. <div class="goods_main_picture">
  131. <div
  132. class="preview-box"
  133. @mousemove="move($event)"
  134. @mouseleave="out($event)"
  135. @mouseenter="enter($event)"
  136. ref="previewBox"
  137. >
  138. <div
  139. class="imageBorder"
  140. :class="{
  141. default_image: true,
  142. skeleton_default_image: firstLoading,
  143. }"
  144. :style="{ backgroundImage: 'url(' + defaultImage + ')' }"
  145. >
  146. <video
  147. v-if="
  148. currentDefaultImage == -1 && goodsDetail.data.goodsVideo
  149. "
  150. controls
  151. playsinline="playsinline"
  152. class="imageBorder default_image"
  153. :poster="defaultImage"
  154. autoplay
  155. ref="video"
  156. >
  157. <source
  158. :src="goodsDetail.data.goodsVideo"
  159. type="video/mp4"
  160. />
  161. </video>
  162. </div>
  163. <div
  164. class="v_btn"
  165. v-if="
  166. currentDefaultImage != -1 && goodsDetail.data.goodsVideo
  167. "
  168. >
  169. <img src="/goods/playV.png" alt="" @click="playV" />
  170. </div>
  171. <!-- 遮罩 start-->
  172. <div class="mask" ref="maskBox" v-show="maskShow"></div>
  173. <!-- 遮罩 end -->
  174. <!-- 底部放大镜icon图标 start -->
  175. <div
  176. class="magnifier_icon flex_row_center_center"
  177. v-show="!maskShow"
  178. >
  179. <i class="iconfont icon-sousuo"></i>
  180. </div>
  181. <!-- 底部放大镜icon图标 end -->
  182. </div>
  183. <!-- 右侧的放大后的图片 start -->
  184. <div
  185. class="goods_picture_big"
  186. style="border: 1px solid #eee"
  187. ref="zoomBox"
  188. v-show="maskShow"
  189. >
  190. <div
  191. class="default_image_big"
  192. :style="{ backgroundImage: 'url(' + defaultImage + ')' }"
  193. ref="pictureBig"
  194. ></div>
  195. </div>
  196. <!-- 右侧的放大后的图片 end -->
  197. </div>
  198. <!-- 商品放大镜效果 end -->
  199. <!-- 商品图片列表 start -->
  200. <div
  201. :class="{
  202. goods_picture_con: true,
  203. flex_row_between_center: true,
  204. skeleton_goods_picture_con: firstLoading,
  205. }"
  206. v-if="
  207. goodsDetail.data.defaultProduct &&
  208. goodsDetail.data.defaultProduct.goodsPics &&
  209. goodsDetail.data.defaultProduct.goodsPics.length > 0
  210. "
  211. >
  212. <i
  213. class="iconfont icon-ziyuan2 left_arrow"
  214. :class="{ no_left_arrow: currentDefaultImage == 0 }"
  215. @click="switchDefaultImage('left')"
  216. ></i>
  217. <div class="show_box">
  218. <ul
  219. class="goods_picture_list flex_row_start_center"
  220. ref="goodsPictureList"
  221. >
  222. <li
  223. v-for="(goodsImgItem, goodsImgIndex) in goodsDetail.data
  224. .defaultProduct.goodsPics"
  225. :key="goodsImgIndex"
  226. class="goods_img"
  227. :class="{
  228. goods_img_active: currentDefaultImage == goodsImgIndex,
  229. }"
  230. @click="selectDefaultImage(goodsImgItem, goodsImgIndex)"
  231. @mouseover="
  232. selectDefaultImage(goodsImgItem, goodsImgIndex)
  233. "
  234. >
  235. <div
  236. class="goods_image"
  237. :style="{
  238. backgroundImage: 'url(' + goodsImgItem + ')',
  239. }"
  240. ></div>
  241. </li>
  242. </ul>
  243. </div>
  244. <i
  245. class="iconfont icon-ziyuan11 right_arrow"
  246. :class="{
  247. no_left_arrow:
  248. currentDefaultImage ==
  249. goodsDetail.data.defaultProduct.goodsPics.length - 1,
  250. }"
  251. @click="switchDefaultImage('right')"
  252. ></i>
  253. </div>
  254. <!-- 商品图片列表 end -->
  255. <!-- 商品分享和收藏 start -->
  256. <div
  257. class="collection_share_btn flex_row_start_start"
  258. v-if="goodsDetail.data.state == 3"
  259. >
  260. <div
  261. class="collection_btn flex_row_start_center cursor_pointer"
  262. @click="collectGoods"
  263. >
  264. <img
  265. src="/goods/collection.png"
  266. alt=""
  267. v-if="goodsDetail.data.followGoods"
  268. />
  269. <img src="/goods/collection1.png" alt="" v-else />
  270. <span>{{
  271. goodsDetail.data.followGoods ? L["已收藏"] : L["收藏"]
  272. }}</span>
  273. </div>
  274. <div class="share_btn">
  275. <AddThis publicId="ra-5ab34ca22008ed41" />
  276. </div>
  277. </div>
  278. <!-- 商品分享和收藏 end -->
  279. </div>
  280. <!-- 商品图片列表 end -->
  281. <!-- 商品详细信息 start -->
  282. <div class="m_item_inner">
  283. <div class="item_info">
  284. <div
  285. :class="{
  286. detaile_name: true,
  287. skeleton_detaile_name: firstLoading,
  288. }"
  289. >
  290. {{ goodsDetail.data.goodsName }}
  291. </div>
  292. <div
  293. :class="{ p_ad: true, skeleton_p_ad: firstLoading }"
  294. v-if="goodsDetail.data.goodsBrief"
  295. >
  296. {{ goodsDetail.data.goodsBrief }}
  297. </div>
  298. <!-- 商品未下架即正常商品 start -->
  299. <div v-if="goodsDetail.data.state == 3 || firstLoading">
  300. <div class="summary">
  301. <div
  302. class="sld_summary_item summary_goods clearfix"
  303. v-if="goodsDetail.data.defaultProduct"
  304. >
  305. <div class="sld_summary_goods_left">
  306. <div class="goods_price flex_row_start_center">
  307. <!-- 在售价 -->
  308. <div>
  309. <span class="price_title">{{ L["价格"] }}</span>
  310. <strong
  311. :class="{
  312. p_price: true,
  313. skeleton_p_price: firstLoading,
  314. }"
  315. >
  316. <span>
  317. {{
  318. goodsDetail.data.goodsMoney == null
  319. ? "面议"
  320. : goodsDetail.data.goodsMoney
  321. }}
  322. </span>
  323. </strong>
  324. </div>
  325. </div>
  326. </div>
  327. </div>
  328. </div>
  329. <div v-if="
  330. goodsDetail.data.goodsMinOrder != undefined &&
  331. goodsDetail.data.goodsMinOrder
  332. " class="goodsMinOrder">
  333. <span class="">{{ L["最低采购数量:"] }}</span>
  334. <strong>
  335. <span>
  336. {{
  337. goodsDetail.data.goodsMinOrder == null
  338. ? "--"
  339. : goodsDetail.data.goodsMinOrder
  340. }}
  341. </span>
  342. </strong>
  343. </div>
  344. <!-- 商品简介 start -->
  345. <div class="summary-info">
  346. <div
  347. v-if="
  348. goodsDetail.data.goodsSummary != undefined &&
  349. goodsDetail.data.goodsSummary
  350. "
  351. class="summary_html"
  352. :style="goodsDetail.data.goodsSummaryBg"
  353. >
  354. <div
  355. class="summary_htmls"
  356. v-html="goodsDetail.data.goodsSummary"
  357. ></div>
  358. </div>
  359. </div>
  360. <!-- 规格 start -->
  361. <div
  362. class="goods_spec"
  363. v-if="
  364. goodsDetail.data.specs &&
  365. goodsDetail.data.specs.length > 0
  366. "
  367. >
  368. <div
  369. class="goods_spec_pre flex_row_start_start"
  370. v-for="(specItem, specIndex) in goodsDetail.data.specs"
  371. :key="specIndex"
  372. >
  373. <div
  374. class="goods_spec_pre_title"
  375. :title="specItem.specName"
  376. >
  377. {{ specItem.specName }}
  378. </div>
  379. <div class="goods_spec_pre_list flex_row_start_center">
  380. <!-- checkState : 1-选中,2-可选,3-禁用 -->
  381. <!-- 禁止选择 -->
  382. <div
  383. class="specval_pre cursor_pointer"
  384. :class="{
  385. specval_pre_disabled: sepcValItem.checkState == '3',
  386. }"
  387. v-for="(
  388. sepcValItem, specValIndex
  389. ) in specItem.specValueList"
  390. :key="specValIndex"
  391. v-show="sepcValItem.checkState == '3'"
  392. >
  393. <div
  394. class="specval_pre_image"
  395. :style="{
  396. backgroundImage: 'url(' + sepcValItem.image + ')',
  397. }"
  398. :title="sepcValItem.specValue"
  399. v-if="sepcValItem.image"
  400. ></div>
  401. <span class="specval_pre_text" v-else>{{
  402. sepcValItem.specValue
  403. }}</span>
  404. </div>
  405. <!-- 可选择 -->
  406. <div
  407. class="specval_pre cursor_pointer"
  408. :class="{
  409. specval_pre_active: sepcValItem.checkState == '1',
  410. }"
  411. v-for="(
  412. sepcValItem, specValIndex
  413. ) in specItem.specValueList"
  414. :key="specValIndex"
  415. @click="
  416. selectSpecVal(
  417. 'choice',
  418. specItem.specId,
  419. sepcValItem.specValueId
  420. )
  421. "
  422. v-show="sepcValItem.checkState != '3'"
  423. >
  424. <div
  425. class="goods_image"
  426. :style="{
  427. backgroundImage: 'url(' + goodsImgItem + ')',
  428. }"
  429. ></div>
  430. <div
  431. class="specval_pre_image"
  432. :style="{
  433. backgroundImage: 'url(' + sepcValItem.image + ')',
  434. }"
  435. :title="sepcValItem.specValue"
  436. v-if="sepcValItem.image"
  437. ></div>
  438. <span class="specval_pre_text" v-else>{{
  439. sepcValItem.specValue
  440. }}</span>
  441. <img
  442. src="/goods/check_mark.png"
  443. alt=""
  444. class="check_mark"
  445. v-if="sepcValItem.checkState == '1'"
  446. />
  447. </div>
  448. </div>
  449. </div>
  450. </div>
  451. <!-- 规格 end -->
  452. </div>
  453. <!-- 商品未下架即正常商品 end -->
  454. <template v-if="!firstLoading">
  455. <!-- 立即购买 加入购物车 收藏 ,分享 start-->
  456. <!-- 商品已下架 start -->
  457. <div
  458. class="options_btn"
  459. v-if="goodsDetail.data.state && goodsDetail.data.state != 3"
  460. >
  461. <p class="option_desc">{{L['商品已下架,欢迎挑选其他商品~']}}</p>
  462. <div class="goods_off_shelves">{{ L["商品已下架"] }}</div>
  463. <!-- 商品下架时的推荐商品 start -->
  464. <div class="recoOffShop">
  465. <div
  466. class="reCon"
  467. v-for="(
  468. {
  469. goodsImage,
  470. goodsName,
  471. goodsPrice,
  472. defaultProductId,
  473. },
  474. index
  475. ) in recomOffShop.data"
  476. :key="index"
  477. >
  478. <div class="reComImg">
  479. <router-link
  480. :to="'/goods/detail/'+ calcProductName(goodsName) +'_'+ defaultProductId"
  481. target="_blank"
  482. >
  483. <img :src="goodsImage" alt="" />
  484. </router-link>
  485. </div>
  486. <router-link
  487. class="recomName"
  488. :to="'/goods/detail/'+ calcProductName(goodsName) +'_'+ defaultProductId"
  489. target="_blank"
  490. >
  491. {{ goodsName }}
  492. </router-link>
  493. <span class="recomPrice">{{ goodsMoney }}</span>
  494. </div>
  495. </div>
  496. <!-- 商品下架时的推荐商品 end -->
  497. </div>
  498. <!-- 商品已下架 end -->
  499. <!-- 普通(活动)正常商品 start -->
  500. <div class="options_btn flex_row_start_center" v-else>
  501. <div
  502. class="goods_code"
  503. id="qrcodeAct"
  504. v-show="isShowQr"
  505. ></div>
  506. <div class="buy_now flex_row_center_center mt-20" @click="goBuy">
  507. {{ L["发送询盘"] }}
  508. </div>
  509. </div>
  510. <!-- 普通(活动)正常商品 end -->
  511. <!-- 立即购买 加入购物车 收藏 ,分享 end-->
  512. </template>
  513. </div>
  514. </div>
  515. <!-- 商品详细信息 end -->
  516. <!-- 相关推荐 start -->
  517. <div class="more_goods" v-if="goodsDetail.data.state == 3">
  518. <div class="more_goods_title">{{ L["看了又看"] }}</div>
  519. <div class="more_goods_list flex_column_center_center">
  520. <template v-if="firstLoading">
  521. <div
  522. class="more_goods_pre"
  523. v-for="(recommendItem, recommendIndex) in [
  524. { a: 1 },
  525. { b: 2 },
  526. { c: 3 },
  527. ]"
  528. :key="recommendIndex"
  529. >
  530. <router-link
  531. target="_blank"
  532. :to="'/goods/detail/'+ calcProductName(recommendItem.goodsName) +'_'+ recommendItem.defaultProductId"
  533. >
  534. <div
  535. :class="{
  536. more_goods_pre_img: true,
  537. skeleton_more_goods_pre_img: firstLoading,
  538. }"
  539. ></div>
  540. <p
  541. :class="{
  542. skeleton_more_goods_pre_goods_name: firstLoading,
  543. }"
  544. >
  545. {{ recommendItem.goodsName }}
  546. </p>
  547. <p>
  548. <span
  549. :class="{
  550. skeleton_more_goods_pre_goods_price: firstLoading,
  551. }"
  552. ></span>
  553. </p>
  554. </router-link>
  555. </div>
  556. </template>
  557. <template v-else>
  558. <div
  559. class="more_goods_pre"
  560. v-for="(
  561. recommendItem, recommendIndex
  562. ) in recommendeList.data"
  563. :key="recommendIndex"
  564. v-show="recommendIndex < 2"
  565. >
  566. <router-link
  567. target="_blank"
  568. :to="'/goods/detail/'+ calcProductName(recommendItem.goodsName) +'_'+ recommendItem.defaultProductId"
  569. >
  570. <div class="more_goods_pre_img flex_row_center_center">
  571. <img
  572. :src="recommendItem.goodsImage"
  573. :title="recommendItem.goodsName"
  574. />
  575. </div>
  576. <p>{{ recommendItem.goodsName }}</p>
  577. <p>{{ recommendItem.goodsMoney }}</p>
  578. </router-link>
  579. </div>
  580. </template>
  581. </div>
  582. </div>
  583. <!-- 相关推荐 end -->
  584. </div>
  585. <!-- 商品相关 end -->
  586. <!-- 店铺,及各种信息的切换 start -->
  587. <div
  588. class="container flex_row_start_start"
  589. ref="container"
  590. id="container"
  591. >
  592. <div class="left">
  593. <div class="store_info" v-if="goodsDetail.data.storeInf">
  594. <div class="store_info_title flex_row_start_center">
  595. <span
  596. class="store_type"
  597. v-if="goodsDetail.data.storeInf.isOwnStore == 1"
  598. >{{ L["自营"] }}</span
  599. >
  600. <router-link
  601. target="_blank"
  602. :to="'/store/'+ calcProductName(goodsDetail.data.storeInf.storeName) +'_'+ goodsDetail.data.storeInf.storeId"
  603. >
  604. <span class="store_title" @click="goStore()">{{
  605. goodsDetail.data.storeInf.storeName
  606. }}</span>
  607. </router-link>
  608. </div>
  609. <div class="store_des">
  610. <div class="store_des_pre pre_service">
  611. <span>{{ L["客服电话"] }}:</span>
  612. <span>{{ goodsDetail.data.storeInf.servicePhone }}</span>
  613. </div>
  614. <div class="store_des_pre pre_service">
  615. <span>{{ L["主营商品"] }}:</span>
  616. <span>{{ goodsDetail.data.storeInf.mainBusiness }}</span>
  617. </div>
  618. </div>
  619. <div class="store_btn flex_row_center_center">
  620. <div class="store_btn_pre flex_row_center_center">
  621. <img src="/goods/store.png" alt="" class="btn_pre_img" />
  622. <router-link
  623. target="_blank"
  624. :to="'/store/'+ calcProductName(goodsDetail.data.storeInf.storeName) +'_'+ goodsDetail.data.storeInf.storeId"
  625. class="go_store_btn"
  626. >
  627. {{ L["进入店铺"] }}
  628. </router-link>
  629. </div>
  630. <div
  631. class="store_btn_pre flex_row_center_center"
  632. @click="focusStore"
  633. >
  634. <img
  635. src="/goods/collection.png"
  636. alt=""
  637. v-if="goodsDetail.data.storeInf.isFollowStore"
  638. />
  639. <img src="/goods/no_collection.png" alt="" v-else />
  640. <span>{{
  641. goodsDetail.data.storeInf.isFollowStore
  642. ? L["取消关注"]
  643. : L["关注店铺"]
  644. }}</span>
  645. </div>
  646. </div>
  647. </div>
  648. <!-- 店铺推荐及热门收藏 end -->
  649. </div>
  650. <!-- 商品详情 评价 商品服务 店铺热销 start-->
  651. <div class="goods_description">
  652. <div class="goods_description_title flex_row_between_center">
  653. <div class="description_title_left flex_row_start_center">
  654. <span
  655. class="cursor_pointer"
  656. :class="{
  657. description_active: goodsDesctionType == 'detail',
  658. }"
  659. @click="goodsDescType('detail')"
  660. >{{ L["商品详情"] }}</span
  661. >
  662. <span
  663. class="cursor_pointer"
  664. v-if="
  665. goodsDetail.data.goodsAnnexList &&
  666. goodsDetail.data.goodsAnnexList.length > 0
  667. "
  668. :class="{
  669. description_active: goodsDesctionType == 'evaluate',
  670. }"
  671. @click="goodsDescType('evaluate')"
  672. >{{ L["商品说明"] }}</span
  673. >
  674. <span
  675. class="cursor_pointer"
  676. v-if="
  677. goodsDetail.data.serviceLabels &&
  678. goodsDetail.data.serviceLabels.length > 0
  679. "
  680. :class="{
  681. description_active: goodsDesctionType == 'service',
  682. }"
  683. @click="goodsDescType('service')"
  684. >{{ L["商品服务"] }}</span
  685. >
  686. <span
  687. class="cursor_pointer"
  688. :class="{
  689. description_active: goodsDesctionType == 'salestore',
  690. }"
  691. @click="goodsDescType('salestore')"
  692. >{{ L["店铺推荐"] }}</span
  693. >
  694. </div>
  695. </div>
  696. <div class="goods_description_con">
  697. <!-- 商品详情,规格参数,及详情富文本 start-->
  698. <div class="goods_des_con" v-if="goodsDesctionType == 'detail'">
  699. <div
  700. v-if="
  701. goodsDetail.data.brandName ||
  702. (goodsDetail.data.goodsParameterList &&
  703. goodsDetail.data.goodsParameterList.length > 0) ||
  704. goodsDetail.data.goodsDetails
  705. "
  706. >
  707. <div class="brand" v-if="goodsDetail.data.brandName">
  708. <span>{{ L["品牌"] }}: </span>
  709. <span>{{ goodsDetail.data.brandName }}</span>
  710. </div>
  711. <div
  712. v-if="
  713. goodsDetail.data.goodsParameterList &&
  714. goodsDetail.data.goodsParameterList.length > 0
  715. "
  716. >
  717. <div
  718. class="goods_parameter_list"
  719. :class="{ goods_paramter_more: !parameterShow }"
  720. >
  721. <div
  722. class="goods_parameter_pre"
  723. v-for="(parameterItem, paramterIndex) in goodsDetail
  724. .data.goodsParameterList"
  725. :key="paramterIndex"
  726. >
  727. <span>{{ parameterItem.parameterName }}: </span>
  728. <span>{{ parameterItem.parameterValue }}</span>
  729. </div>
  730. </div>
  731. <div
  732. class="collapse_unfold flex_row_center_center cursor_pointer"
  733. v-if="goodsDetail.data.goodsParameterList.length > 16"
  734. @click="openParameter"
  735. >
  736. <span>{{
  737. !parameterShow ? L["查看全部"] : L["收起全部"]
  738. }}</span>
  739. <i
  740. class="iconfont icon-ziyuan11-copy"
  741. v-if="!parameterShow"
  742. ></i>
  743. <i class="iconfont icon-ziyuan11-copy-copy" v-else></i>
  744. </div>
  745. </div>
  746. <div
  747. v-if="
  748. goodsDetail.data.topTemplateContent != undefined &&
  749. goodsDetail.data.topTemplateContent
  750. "
  751. class="goods_html"
  752. :style="goodsDetail.data.topTemplateContentBg"
  753. >
  754. <div
  755. class="goods_htmls"
  756. v-html="goodsDetail.data.topTemplateContent"
  757. ></div>
  758. </div>
  759. <div
  760. v-if="
  761. goodsDetail.data.goodsDetails != undefined &&
  762. goodsDetail.data.goodsDetails
  763. "
  764. class="goods_html"
  765. :style="goodsDetail.data.goodsDetailsBg"
  766. >
  767. <div
  768. class="goods_htmls"
  769. v-html="goodsDetail.data.goodsDetails"
  770. ></div>
  771. </div>
  772. <div
  773. v-if="
  774. goodsDetail.data.bottomTemplateContent != undefined &&
  775. goodsDetail.data.bottomTemplateContent
  776. "
  777. class="goods_html"
  778. :style="goodsDetail.data.bottomTemplateContentBg"
  779. >
  780. <div
  781. class="goods_htmls"
  782. v-html="goodsDetail.data.bottomTemplateContent"
  783. ></div>
  784. </div>
  785. </div>
  786. <div v-else>
  787. <SldCommonEmpty
  788. :tip="L['该商品暂无详情~']"
  789. totalWidth="934px"
  790. />
  791. </div>
  792. </div>
  793. <!-- 商品详情,规格参数,及详情富文本 end-->
  794. <!-- 商品评价 start -->
  795. <div
  796. class="goods_comments"
  797. v-if="goodsDesctionType == 'evaluate'"
  798. >
  799. <!--说明书下载begin-->
  800. <div
  801. class="download_warp"
  802. v-if="
  803. goodsDetail.data.goodsAnnexList &&
  804. goodsDetail.data.goodsAnnexList.length > 0
  805. "
  806. >
  807. <span style="margin: 10px; font-size: 14px"
  808. >{{ L["说明书下载"] }}:
  809. </span>
  810. <br />
  811. <p
  812. style="margin: 10px; font-size: 14px"
  813. v-for="(item, index) in goodsDetail.data.goodsAnnexList"
  814. :key="index"
  815. >
  816. <a
  817. style="text-decoration: underline"
  818. href="javascript:;"
  819. @click="downloadAdd(item)"
  820. >{{ item.annexName }}</a
  821. >
  822. </p>
  823. </div>
  824. <div v-else>
  825. {{ L["暂无说明"] }}
  826. </div>
  827. <!--说明书下载end-->
  828. </div>
  829. <!-- 商品评价 end -->
  830. <!-- 商品服务 start -->
  831. <div
  832. class="goods_server_list"
  833. v-if="goodsDesctionType == 'service'"
  834. >
  835. <div
  836. v-if="
  837. goodsDetail.data.serviceLabels &&
  838. goodsDetail.data.serviceLabels.length > 0
  839. "
  840. >
  841. <div
  842. class="goods_server_pre"
  843. v-for="(serverItem, serverIndex) in goodsDetail.data
  844. .serviceLabels"
  845. :key="serverIndex"
  846. >
  847. <div class="server_pre_top flex_row_start_center">
  848. <span class="server_pre_tips"></span>
  849. <span class="server_pre_name">{{
  850. serverItem.labelName
  851. }}</span>
  852. </div>
  853. <div class="server_pre_content">
  854. {{ serverItem.description }}
  855. </div>
  856. </div>
  857. </div>
  858. <div v-else>
  859. <SldCommonEmpty
  860. :tip="L['暂无商品服务~']"
  861. totalWidth="934px"
  862. />
  863. </div>
  864. </div>
  865. <!-- 商品服务 end -->
  866. <!-- 店铺推荐 start -->
  867. <div
  868. class="store_hot_sales"
  869. v-if="goodsDesctionType == 'salestore'"
  870. >
  871. <div
  872. v-if="
  873. recommendedList.data && recommendedList.data.length > 0
  874. "
  875. >
  876. <div class="store_hot_sales_list">
  877. <div
  878. class="goods_pre flex_column_between_start"
  879. v-for="(
  880. recommendItem, recommendIndex
  881. ) in recommendedList.data"
  882. :key="recommendIndex"
  883. >
  884. <router-link
  885. target="_blank"
  886. :to="'/goods/detail/'+ calcProductName(recommendItem.goodsName) +'_'+ recommendItem.defaultProductId"
  887. >
  888. <div class="flex_column_start_start">
  889. <div
  890. class="goods_pre_img"
  891. :style="{
  892. backgroundImage:
  893. 'url(' + recommendItem.goodsImage + ')',
  894. }"
  895. ></div>
  896. <div class="goods_name">
  897. {{ recommendItem.goodsName }}
  898. </div>
  899. </div>
  900. <div class="goods_price">
  901. <div class="selling_price">
  902. {{ recommendItem.goodsMoney }}
  903. </div>
  904. </div>
  905. </router-link>
  906. </div>
  907. </div>
  908. <div
  909. class="flex_row_end_center sld_pagination sld_page_bottom"
  910. v-if="
  911. recommendeData.data.pagination &&
  912. recommendeData.data.pagination.total
  913. "
  914. >
  915. <el-pagination
  916. @current-change="handleCurrentChangeSales"
  917. v-model:currentPage="salesCurrent"
  918. :page-size="salesPageSize"
  919. layout="prev, pager, next, jumper"
  920. :total="recommendeData.data.pagination.total"
  921. :hide-on-single-page="true"
  922. >
  923. </el-pagination>
  924. </div>
  925. </div>
  926. <div
  927. v-if="
  928. recommendedList.data && recommendedList.data.length == 0
  929. "
  930. class="flex_column_center_center empty_data"
  931. >
  932. <SldCommonEmpty
  933. :tip="L['暂无相关商品~']"
  934. totalWidth="934px"
  935. />
  936. </div>
  937. </div>
  938. <!-- 店铺推荐 end -->
  939. </div>
  940. </div>
  941. <!-- 商品详情 评价 商品服务 店铺热销 end-->
  942. </div>
  943. <!-- 店铺,及各种信息的切换 end -->
  944. </div>
  945. <!-- 商品主要信息 end -->
  946. </div>
  947. <SldLoginModal
  948. v-if="loginModalVisibleFlag"
  949. @closeLoingModal="closeLoingModal"
  950. @refreshInfo="refreshInfo"
  951. />
  952. <EnquiryModal
  953. v-if="enquiryVis"
  954. :itemType="'GOODS'"
  955. :itemId="productId"
  956. @closeLoingModal="closeEnquiryModal"
  957. />
  958. </div>
  959. </div>
  960. </template>
  961. <script setup>
  962. import addrData from "@/assets/area.json";
  963. import { ElMessage, ElRate, ElDialog, ElPagination } from "element-plus";
  964. import { qrcanvas } from "qrcanvas";
  965. // import { lang_zn } from "@/assets/language/zh";
  966. import { getCurLanguage } from '@/composables/common.js';
  967. import { goodsInfo, useUserInfo } from "@/store/user.js";
  968. import { useFiltersStore } from "@/store/filter.js";
  969. const filtersStore = useFiltersStore();
  970. const goodsInfox = goodsInfo();
  971. const configInfo = useUserInfo();
  972. // const L = lang_zn;
  973. const L = getCurLanguage();
  974. const firstLoading = ref(true); //是否第一次加载
  975. const router = useRouter();
  976. const route = useRoute();
  977. const store = ref();
  978. const imgVisible = ref(false);
  979. const imgSource = ref("");
  980. const imgIndex = ref(-1);
  981. const vid = ref(0); //店铺id
  982. const proxy = getCurrentInstance();
  983. const goodsDetail = reactive({ data: {} }); //商品详情数据
  984. const isChoice = ref("default"); //是默认选中的,还是点击选择规格之后的 default:默认 choice:选择
  985. const productId = ref(""); //货品id
  986. const fullDisList = reactive({ data: [] }); //满优惠促销列表
  987. const couponList = reactive({ data: [] }); //获取店铺优惠券列表
  988. const pictureBig = ref(null); //大图的信息
  989. const maskBox = ref(null); //遮罩盒子的信息
  990. const maskShow = ref(false); //遮罩是否显示
  991. const previewBox = ref(null); // 左侧主图元素信息
  992. const zoomBox = ref(null); //左侧主图的父元素的信息
  993. const defaultImage = ref(""); //默认主图路径
  994. const currentDefaultImage = ref("0"); //默认主图显示第一张
  995. const currentSpecNum = ref(1); //商品编辑数量,默认数量为1
  996. const recommendeList = reactive({ data: [] }); //看了又看商品
  997. const storePopularList = reactive({ data: [] }); //店铺推荐及热门收藏
  998. const storePopularType = ref("collection"); //店铺推荐及热门收藏,默认显示店铺推荐
  999. const goodsDesctionType = ref("detail"); //商品详情,评价,商品服务,店铺热销,默认显示商品详情
  1000. const goodsCommentsInfo = reactive({ data: {} }); //商品评价信息
  1001. const evaluationType = ref(""); //商品评价类型,默认显示全部
  1002. const recommendedList = reactive({ data: [] }); //店铺推荐列表
  1003. const evaluationCurrent = ref(1); //评价列表默认第一页
  1004. const evaluationPageSize = ref(5); //评价列表默认一页显示5条数据
  1005. const salesCurrent = ref(1); //店铺热销列表默认一页
  1006. const salesPageSize = ref(20); //店铺热销列表默认一页显示20条数据
  1007. const recommendeData = reactive({ data: {} }); //店铺热销推荐数据
  1008. const couponModel = ref(false); //优惠券弹框是否显示
  1009. const cartListData = reactive({ data: goodsInfox.cartListData }); //获取vux的store中的购物车数据
  1010. const loginModalVisibleFlag = ref(false); //登录弹框是否显示,默认不显示
  1011. const enquiryVis = ref(false); //登录弹框是否显示,默认不显示
  1012. const container = ref(null); // 商品详情页底部内容区
  1013. const containerTop = ref(0); //商品详情页底部内容区的top值
  1014. const containCon = ref(false); //固定内容区头部
  1015. const fullDiscountModel = ref(false); //满优惠弹框是否显示
  1016. const goodsPictureList = ref(null); //商品图片列表
  1017. const curCouponPage = ref(1); //当前为第一页优惠券
  1018. const couponPageSize = ref(6); //优惠券默认一页显示6条数据
  1019. const wxShareCode = ref(false); //微信分享二维码是否显示
  1020. const parameterShow = ref(false); //规格参数查看是否查看全部,默认为否
  1021. const score = ref(0); //好评率
  1022. const colors = ref(["#E2231A", "#E2231A", "#E2231A"]); //星星颜色
  1023. const curAddr = ref(-1);
  1024. const curAddrName = ref("");
  1025. const addrIdx = ref(0);
  1026. const otherAddrIdx = ref(0);
  1027. const othTopIdx = reactive({
  1028. 0: 0,
  1029. 1: 0,
  1030. 2: 0,
  1031. });
  1032. const otherTree = ref([
  1033. addrData[othTopIdx["0"]],
  1034. addrData[othTopIdx["0"]].children[othTopIdx["1"]],
  1035. ]);
  1036. const othAddrDe = ref(addrData);
  1037. const addrDialogVisible = ref(false);
  1038. const logFlag = ref(configInfo.loginFlag);
  1039. // 促销活动信息
  1040. const preSellInfo = reactive({ data: {} });
  1041. const pinInfo = reactive({ data: {} });
  1042. const seckillInfo = reactive({ data: {} });
  1043. const ladderInfo = reactive({ data: {} });
  1044. const address_list = reactive({ data: [] });
  1045. const isShowQr = ref(false);
  1046. const secInt = ref("");
  1047. const time = reactive({
  1048. day: "00",
  1049. hours: "00",
  1050. minutes: "00",
  1051. seconds: "00",
  1052. });
  1053. const judgeStock = computed(() => {
  1054. return (
  1055. goodsDetail.data.defaultProduct.productStock == 0 ||
  1056. (JSON.stringify(preSellInfo.data) != "{}" &&
  1057. preSellInfo.data.presellStock == 0) ||
  1058. (JSON.stringify(pinInfo.data) != "{}" && pinInfo.data.spellStock == 0) ||
  1059. (JSON.stringify(seckillInfo.data) != "{}" &&
  1060. seckillInfo.data.seckillStock == 0)
  1061. );
  1062. });
  1063. // 促销活动信息end
  1064. const scrollHandle = async (e) => {
  1065. if (process.client) {
  1066. let elementScrollTop = e.srcElement.scrollingElement.scrollTop; //获取页面滚动高度
  1067. if (
  1068. document.getElementById("container") &&
  1069. elementScrollTop > document.getElementById("container").offsetTop
  1070. ) {
  1071. containCon.value = true;
  1072. await proxy.$nextTick();
  1073. } else {
  1074. containCon.value = false;
  1075. }
  1076. }
  1077. };
  1078. // 点击播放视频
  1079. const playV = () => {
  1080. currentDefaultImage.value = -1;
  1081. defaultImage.value = "";
  1082. maskShow.value = false;
  1083. videoEnd();
  1084. };
  1085. //获取商品详情数据
  1086. const getInitDataStatic = async (proId) => {
  1087. let params = {
  1088. productId: proId,
  1089. };
  1090. const { data: value, pending: pending } = await useFetchRaw(
  1091. apiUrl + "v3/goods/front/goods/details",
  1092. { params: params }
  1093. );
  1094. const res = value._rawValue;
  1095. if (res.state == 200) {
  1096. let staticData = [
  1097. "brandId",
  1098. "brandName",
  1099. "categoryPath",
  1100. "categoryIdPath",
  1101. "goodsBrief",
  1102. "goodsDetails",
  1103. "goodsSummary",
  1104. "goodsBrief",
  1105. "goodsId",
  1106. "goodsName",
  1107. "goodsParameterList",
  1108. "goodsVideo",
  1109. "topTemplateContent",
  1110. "bottomTemplateContent",
  1111. "goodsAnnexList",
  1112. "serviceLabels",
  1113. "goodsMoney",
  1114. "goodsMinOrder",
  1115. "goodsParameterList",
  1116. ];
  1117. staticData.forEach((item) => {
  1118. if (item == "categoryPath") {
  1119. goodsDetail.data.categoryPath = res.data.categoryPath.split("->");
  1120. } else if (item == "categoryIdPath") {
  1121. goodsDetail.data.categoryIdPath = [
  1122. res.data.categoryId1,
  1123. res.data.categoryId2,
  1124. res.data.categoryId3,
  1125. ];
  1126. } else {
  1127. goodsDetail.data[item] = res.data[item];
  1128. }
  1129. });
  1130. if (goodsDetail.data.goodsSummary) {
  1131. goodsDetail.data.goodsSummary = quillEscapeToHtml(
  1132. goodsDetail.data.goodsSummary
  1133. );
  1134. //处理背景样式
  1135. if (
  1136. goodsDetail.data.goodsSummary.indexOf(
  1137. '<p style="display:none;" data-background="'
  1138. ) != -1
  1139. ) {
  1140. let bg = goodsDetail.data.goodsSummary
  1141. .split('<p style="display:none;" data-background="')[1]
  1142. .split('">')[0];
  1143. goodsDetail.data.goodsSummaryBg = bg;
  1144. }
  1145. }
  1146. if (goodsDetail.data.topTemplateContent) {
  1147. goodsDetail.data.topTemplateContent = quillEscapeToHtml(
  1148. goodsDetail.data.topTemplateContent
  1149. );
  1150. //处理背景样式
  1151. if (
  1152. goodsDetail.data.topTemplateContent.indexOf(
  1153. '<p style="display:none;" data-background="'
  1154. ) != -1
  1155. ) {
  1156. let bg = goodsDetail.data.topTemplateContent
  1157. .split('<p style="display:none;" data-background="')[1]
  1158. .split('">')[0];
  1159. goodsDetail.data.topTemplateContentBg = bg;
  1160. }
  1161. }
  1162. if (goodsDetail.data.goodsDetails) {
  1163. goodsDetail.data.goodsDetails = quillEscapeToHtml(
  1164. goodsDetail.data.goodsDetails
  1165. );
  1166. //处理背景样式
  1167. if (
  1168. goodsDetail.data.goodsDetails.indexOf(
  1169. '<p style="display:none;" data-background="'
  1170. ) != -1
  1171. ) {
  1172. let bg = goodsDetail.data.goodsDetails
  1173. .split('<p style="display:none;" data-background="')[1]
  1174. .split('">')[0];
  1175. goodsDetail.data.goodsDetailsBg = bg;
  1176. }
  1177. }
  1178. if (goodsDetail.data.bottomTemplateContent) {
  1179. goodsDetail.data.bottomTemplateContent = quillEscapeToHtml(
  1180. goodsDetail.data.bottomTemplateContent
  1181. );
  1182. //处理背景样式
  1183. if (
  1184. goodsDetail.data.bottomTemplateContent.indexOf(
  1185. '<p style="display:none;" data-background="'
  1186. ) != -1
  1187. ) {
  1188. let bg = goodsDetail.data.bottomTemplateContent
  1189. .split('<p style="display:none;" data-background="')[1]
  1190. .split('">')[0];
  1191. goodsDetail.data.bottomTemplateContentBg = bg;
  1192. }
  1193. }
  1194. currentDefaultImage.value = 0;
  1195. vid.value = res.data.storeInf.storeId;
  1196. // setTimeout(() => {
  1197. // sldStatEvent({ behaviorType: 'gpv', goodsId: goodsDetail.data.goodsId, storeId: vid.value });
  1198. // }, 3000)
  1199. } else {
  1200. ElMessage.error(res.msg);
  1201. }
  1202. if (!pending._rawValue) {
  1203. getInitDataDynamic(productId.value);
  1204. getRecommend();
  1205. getStorePopular();
  1206. getEvaluation();
  1207. addLog();
  1208. }
  1209. };
  1210. getInitDataStatic(calcProductId(route.path));
  1211. onMounted(() => {
  1212. setTimeout(() => {
  1213. sldStatEvent({ behaviorType: 'gpv', goodsId: goodsDetail.data.goodsId, storeId: vid.value,pageUrl: defaultUrl + router.currentRoute.value.path, referrerPageUrl: apiUrl });
  1214. }, 3000)
  1215. });
  1216. productId.value = calcProductId(route.path);
  1217. if (!logFlag.value) {
  1218. addrIdx.value = 1;
  1219. }
  1220. const getInitDataDynamic = async (proId, updateType) => {
  1221. let params = {
  1222. productId: proId,
  1223. };
  1224. const { data: value, pending: pending } = await useFetchRaw(
  1225. apiUrl + "v3/goods/front/goods/details2",
  1226. { params: params,
  1227. headers:{Authorization:'Bearer ' + filtersStore.getToken}
  1228. }
  1229. );
  1230. const res = value._rawValue;
  1231. if (res.state == 200) {
  1232. useHead({
  1233. title: res.data.seoInfo.seoTitle || 'Goods Detail',
  1234. meta: [
  1235. {
  1236. name: "description",
  1237. content: res.data.seoInfo.seoDesc,
  1238. },
  1239. {
  1240. name: "keywords",
  1241. content: res.data.seoInfo.seoKeywords,
  1242. },
  1243. ],
  1244. });
  1245. defaultImage.value = res.data.defaultProduct.goodsPics[0];
  1246. let dynamicData = [
  1247. "defaultProduct",
  1248. "deliverInfo",
  1249. "effectSpecValueIds",
  1250. "followGoods",
  1251. "specs",
  1252. "storeInf",
  1253. "sales",
  1254. "state",
  1255. "shareLink",
  1256. "shareImage",
  1257. "goodsMinOrder",
  1258. "isVirtualGoods",
  1259. ];
  1260. dynamicData.forEach((item) => {
  1261. goodsDetail.data[item] = res.data[item];
  1262. });
  1263. if (goodsDetail.data.state != 3) {
  1264. getRecom();
  1265. }
  1266. if (goodsDetail.data.defaultProduct.promotionType == 103) {
  1267. getPreSell(res.data.defaultProduct.productId);
  1268. } else if (goodsDetail.data.defaultProduct.promotionType == 102) {
  1269. getPin();
  1270. } else if (goodsDetail.data.defaultProduct.promotionType == 104) {
  1271. getSeckill();
  1272. } else if (goodsDetail.data.defaultProduct.promotionType == 105) {
  1273. getLadder();
  1274. } else {
  1275. preSellInfo.data = {};
  1276. pinInfo.data = {};
  1277. seckillInfo.data = {};
  1278. ladderInfo.data = {};
  1279. }
  1280. firstLoading.value = false;
  1281. }
  1282. };
  1283. //视频播放结束时触发
  1284. const videoEnd = () => {
  1285. nextTick(() => {
  1286. proxy.refs.video.onended = () => {
  1287. currentDefaultImage.value = 0;
  1288. defaultImage.value = goodsDetail.data.defaultProduct.goodsPics[0];
  1289. };
  1290. });
  1291. };
  1292. // 促销活动信息
  1293. const getPreSell = async (productId) => {
  1294. let param = {
  1295. productId: productId,
  1296. promotionId: goodsDetail.data.defaultProduct.promotionId,
  1297. };
  1298. const { data: value } = await useFetchRaw(
  1299. apiUrl + "v3/promotion/front/preSell/detail",
  1300. { params: param }
  1301. );
  1302. const res = value.value._rawValue;
  1303. if (res.state == 200) {
  1304. let now = new Date();
  1305. let preStartDate = new Date(res.data.startTime);
  1306. let preEndDate = new Date(res.data.endTime);
  1307. preSellInfo.data = res.data;
  1308. let countTime = 0;
  1309. preSellInfo.data.endTime = formatPreTime(new Date(res.data.endTime));
  1310. preSellInfo.data.startTime = formatPreTime(new Date(res.data.startTime));
  1311. if (now > preStartDate && now < preEndDate) {
  1312. preSellInfo.data.pre_run = 2; //活动进行中
  1313. countTime = res.data.distanceEndTime;
  1314. countDown(countTime);
  1315. } else if (now < preStartDate) {
  1316. preSellInfo.data.pre_run = 1; //活动未开始
  1317. countTime =
  1318. (new Date(res.data.startTime).getTime() - now.getTime()) / 1000;
  1319. countDown(countTime);
  1320. } else if (now > preEndDate) {
  1321. preSellInfo.data.pre_run = 3; //活动已结束
  1322. }
  1323. genQrcode();
  1324. } else {
  1325. ElMessage.error(res.msg);
  1326. }
  1327. };
  1328. const getPin = () => {
  1329. let param = {
  1330. productId: goodsDetail.data.defaultProduct.productId,
  1331. promotionId: goodsDetail.data.defaultProduct.promotionId,
  1332. };
  1333. get("v3/promotion/front/spell/detail", param).then((res) => {
  1334. if (res.state == 200) {
  1335. pinInfo.data = res.data;
  1336. let countTime = 0;
  1337. let now = new Date();
  1338. let startTime = new Date(res.data.startTime);
  1339. if (now < startTime) {
  1340. countTime = (startTime.getTime() - now.getTime()) / 1000;
  1341. countDown(countTime);
  1342. } else {
  1343. countTime = res.data.distanceEndTime;
  1344. countDown(countTime);
  1345. }
  1346. genQrcode();
  1347. } else {
  1348. ElMessage.error(res.msg);
  1349. }
  1350. });
  1351. };
  1352. const getSeckill = () => {
  1353. let param = {
  1354. productId: goodsDetail.data.defaultProduct.productId,
  1355. promotionId: goodsDetail.data.defaultProduct.promotionId,
  1356. };
  1357. get("v3/promotion/front/seckill/detail", param).then((res) => {
  1358. if (res.state == 200) {
  1359. seckillInfo.data = res.data;
  1360. let now = new Date();
  1361. let countTime = 0;
  1362. let startTime = new Date(res.data.startTime);
  1363. if (seckillInfo.data.state == 1 || seckillInfo.data.state == 2) {
  1364. countTime = res.data.distanceEndTime;
  1365. countDown(countTime);
  1366. } else {
  1367. countTime = startTime.getTime() - now.getTime();
  1368. }
  1369. genQrcode();
  1370. }
  1371. });
  1372. };
  1373. const countDown = (countTime) => {
  1374. secInt.value = setInterval(() => {
  1375. if (countTime == 0) {
  1376. getInitDataDynamic(calcProductId(route.path));
  1377. clearInterval(secInt.value);
  1378. } else {
  1379. countTime--;
  1380. let day = parseInt(countTime / 60 / 60 / 24);
  1381. let hours = parseInt((countTime / 60 / 60) % 24);
  1382. let minutes = parseInt((countTime / 60) % 60);
  1383. let seconds = parseInt(countTime % 60);
  1384. time.day = day;
  1385. time.hours = hours > 9 ? hours : "0" + hours;
  1386. time.minutes = minutes > 9 ? minutes : "0" + minutes;
  1387. time.seconds = seconds > 9 ? seconds : "0" + seconds;
  1388. }
  1389. }, 1000);
  1390. };
  1391. const formatPreTime = (time) => {
  1392. let op = new Date(time);
  1393. let year = op.getFullYear();
  1394. let month = op.getMonth() + 1;
  1395. let day = op.getDate();
  1396. let hour = op.getHours();
  1397. let minute = op.getMinutes();
  1398. let part1 = [year, month, day]
  1399. .map((i) => (i.toString().length < 2 ? `0${i}` : i))
  1400. .join("-");
  1401. let part2 = [hour, minute]
  1402. .map((i) => (i.toString().length < 2 ? `0${i}` : i))
  1403. .join(":");
  1404. return part1 + " " + part2;
  1405. };
  1406. const getLadder = () => {
  1407. let param = {
  1408. productId: goodsDetail.data.defaultProduct.productId,
  1409. promotionId: goodsDetail.data.defaultProduct.promotionId,
  1410. };
  1411. get("v3/promotion/front/ladder/group/detail", param).then((res) => {
  1412. if (res.state == 200) {
  1413. ladderInfo.data = res.data;
  1414. let now = new Date();
  1415. let countTime = 0;
  1416. let startTime = new Date(res.data.startTime);
  1417. if (now < startTime) {
  1418. countTime = (startTime.getTime() - now.getTime()) / 1000;
  1419. countDown(countTime);
  1420. ladderInfo.data.state = 1;
  1421. } else {
  1422. countTime = res.data.distanceEndTime;
  1423. countDown(countTime);
  1424. ladderInfo.data.state = 2;
  1425. }
  1426. genQrcode();
  1427. }
  1428. });
  1429. };
  1430. const genQrcode = () => {
  1431. if (judgeStock.value) {
  1432. return;
  1433. }
  1434. proxy.$nextTick(() => {
  1435. let canvas = qrcanvas({
  1436. data: goodsDetail.data.shareLink, //二维码内容
  1437. size: 125,
  1438. colorDark: "red",
  1439. });
  1440. setTimeout(() => {
  1441. document.getElementById("qrcodeAct").innerHTML = "";
  1442. document.getElementById("qrcodeAct").appendChild(canvas);
  1443. }, 10);
  1444. });
  1445. };
  1446. // 促销活动信息end
  1447. const recomOffShop = reactive({ data: [] });
  1448. const getRecom = () => {
  1449. get("v3/goods/front/goods/goodsList", {
  1450. storeId: goodsDetail.data.categoryId1,
  1451. }).then((res) => {
  1452. if (res.state == 200) {
  1453. let top = Math.floor(Math.random() * (res.data.list.length - 8)) + 8;
  1454. let end = top - 8;
  1455. recomOffShop.data = res.data.list
  1456. .filter(
  1457. (item) =>
  1458. item.defaultProductId != goodsDetail.data.defaultProduct.productId
  1459. )
  1460. .slice(end, top);
  1461. }
  1462. });
  1463. };
  1464. //添加足迹
  1465. const addLog = () => {
  1466. let params = {
  1467. productId: productId.value,
  1468. };
  1469. post("v3/member/front/productLookLog/add", params).then((res) => {});
  1470. };
  1471. //记录下载
  1472. const downloadAdd = (item) => {
  1473. let url = item.annexUrl;
  1474. let name = item.annexName;
  1475. if (filtersStore.getLoginFlag) {
  1476. post("v3/member/front/download/add", {
  1477. goodsId: item.goodsId,
  1478. annexId: item.annexId,
  1479. }).then((res) => {
  1480. });
  1481. }
  1482. const link = document.createElement("a");
  1483. fetch(url)
  1484. .then((res) => res.blob())
  1485. .then((blob) => {
  1486. link.href = URL.createObjectURL(blob);
  1487. link.download = name;
  1488. document.body.appendChild(link);
  1489. link.click();
  1490. window.URL.revokeObjectURL(link.href);
  1491. document.body.removeChild(link);
  1492. });
  1493. };
  1494. /**
  1495. * 选择规格值
  1496. * @param type:类型 值:choice,规格选择 default:默认
  1497. * @param specId:父级规格值
  1498. * @param specValueId:点击的当前的规格值
  1499. */
  1500. const selectSpecVal = (type, specId, specValueId) => {
  1501. isChoice.value = type == "choice" ? "choice" : "default";
  1502. let curParSpec = []; //当前点击的规格的父级id的当前项
  1503. curParSpec = goodsDetail.data.specs.filter((item) => item.specId == specId);
  1504. let curSPec = []; //当前点击的规格的规格id的当前项
  1505. curSPec = curParSpec[0].specValueList.filter(
  1506. (item1) => item1.specValueId == specValueId
  1507. );
  1508. curSPec[0].checkState = 1;
  1509. //被选择的规格值的id
  1510. let choiceSpecIds = [];
  1511. goodsDetail.data.specs.forEach((item) => {
  1512. if (item.specId != specId) {
  1513. item.specValueList.forEach((item1) => {
  1514. if (item1.checkState == "1") {
  1515. // checkState: 1-选中,2-可选,3-禁用
  1516. choiceSpecIds.push(item1.specValueId);
  1517. }
  1518. });
  1519. } else {
  1520. choiceSpecIds.push(specValueId);
  1521. }
  1522. });
  1523. let params = {
  1524. goodsId: goodsDetail.data.goodsId,
  1525. specValueIds: choiceSpecIds.join(","),
  1526. };
  1527. get("v3/goods/front/goods/productInfo", params).then((res) => {
  1528. if (res.state == 200) {
  1529. let result = res.data;
  1530. goodsDetail.data.defaultProduct = result.defaultProduct;
  1531. productId.value = result.defaultProduct.productId;
  1532. goodsDetail.data.specs = result.specs;
  1533. defaultImage.value = goodsDetail.data.defaultProduct.goodsPics[0];
  1534. currentDefaultImage.value = 0;
  1535. isShowQr.value = false;
  1536. // getInitDataDynamic(productId.value)
  1537. }
  1538. });
  1539. };
  1540. //改变数量按钮样式
  1541. const disStyle = reactive({
  1542. //目的是进入商品详情页面就让减按钮呈现禁止状态
  1543. color: "#DDDDDD",
  1544. backgroundColor: "#F8F8F8",
  1545. });
  1546. watch(
  1547. () => currentSpecNum.value,
  1548. () => {
  1549. //监听数量对加和减的样式做出调整
  1550. let productStock = goodsDetail.data.defaultProduct.productStock;
  1551. if (goodsDetail.data.defaultProduct.productStock == 0) {
  1552. productStock = 999;
  1553. }
  1554. if (currentSpecNum.value >= productStock) {
  1555. proxy.refs.add.style.color = "#DDDDDD";
  1556. proxy.refs.add.style.backgroundColor = "#F8F8F8";
  1557. } else if (currentSpecNum.value <= 1) {
  1558. disStyle.color = "#DDDDDD";
  1559. disStyle.backgroundColor = "#F8F8F8";
  1560. } else {
  1561. disStyle.color = "";
  1562. disStyle.backgroundColor = "";
  1563. proxy.refs.add.style.color = "";
  1564. proxy.refs.add.style.backgroundColor = "";
  1565. }
  1566. }
  1567. );
  1568. watch(currentSpecNum, () => {
  1569. if (currentSpecNum.value > goodsDetail.data.defaultProduct.productStock) {
  1570. currentSpecNum.value = goodsDetail.data.defaultProduct.productStock;
  1571. }
  1572. let reg = /\./g;
  1573. let reg0 = /0+\d/;
  1574. if (
  1575. currentSpecNum.value &&
  1576. (reg.test(currentSpecNum.value) || currentSpecNum.value <= 0)
  1577. ) {
  1578. currentSpecNum.value = 1;
  1579. }
  1580. });
  1581. //发送询盘
  1582. const goBuy = () => {
  1583. enquiryVis.value = true;
  1584. };
  1585. //关闭登录弹框
  1586. const closeLoingModal = () => {
  1587. loginModalVisibleFlag.value = false;
  1588. };
  1589. //关闭登录弹框
  1590. const closeEnquiryModal = () => {
  1591. enquiryVis.value = false;
  1592. };
  1593. //获取看了又看商品(人气数)
  1594. const getRecommend = async () => {
  1595. let params = {
  1596. storeId: vid.value,
  1597. sort: 5,
  1598. pageSize: 3,
  1599. current: 1,
  1600. };
  1601. const { data: value } = await useFetchRaw(
  1602. apiUrl + "v3/goods/front/goods/goodsList",
  1603. { params: params, key: params.sort.toString() }
  1604. );
  1605. const res = value._rawValue;
  1606. if (res.state == 200) {
  1607. let result = res.data;
  1608. recommendeList.data = result.list;
  1609. recommendeList.data.map(
  1610. (item) => (item.goodsPrice = new Number(item.goodsPrice).toFixed(2))
  1611. );
  1612. } else {
  1613. ElMessage.error(res.msg);
  1614. }
  1615. };
  1616. //获取店铺推荐的商品(销量数);获取热门收藏的商品(收藏数) type:recommend销量数 collection:收藏数
  1617. const getStorePopular = async (type) => {
  1618. if (type == "" || !type) {
  1619. type = "recommend";
  1620. } else {
  1621. storePopularType.value = type;
  1622. }
  1623. let params = {
  1624. storeId: vid.value,
  1625. sort:
  1626. storePopularType.value == "recommend"
  1627. ? 1
  1628. : storePopularType.value == "collection"
  1629. ? 6
  1630. : "",
  1631. pageSize: 6,
  1632. current: 1,
  1633. };
  1634. const { data: value } = await useFetchRaw(
  1635. apiUrl + "v3/goods/front/goods/goodsList",
  1636. { params: params, key: params.sort.toString() }
  1637. );
  1638. const res = value._rawValue;
  1639. if (res.state == 200) {
  1640. let result = res.data;
  1641. storePopularList.data = result.list;
  1642. storePopularList.data.map(
  1643. (item) => (item.goodsPrice = new Number(item.goodsPrice).toFixed(2))
  1644. );
  1645. } else {
  1646. ElMessage.error(res.msg);
  1647. }
  1648. };
  1649. //切换商品详情,评价,服务,热销
  1650. const goodsDescType = (type) => {
  1651. // let elementScrollTop =
  1652. // window.pageYOffset ||
  1653. // document.documentElement.scrollTop ||
  1654. // document.body.scrollTop; //获取页面滚动高度
  1655. // if (elementScrollTop > containerTop.value + 33 + 48) {
  1656. // window.scrollTo({
  1657. // top: containerTop.value,
  1658. // behavior: "smooth",
  1659. // });
  1660. // }
  1661. if (type == "" || !type) {
  1662. goodsDesctionType.value = "detail";
  1663. } else {
  1664. goodsDesctionType.value = type;
  1665. }
  1666. if (type == "salestore") {
  1667. getSalestore();
  1668. }
  1669. };
  1670. //获取商品评价
  1671. const getEvaluation = (type) => {
  1672. if (!type) {
  1673. evaluationType.value = "";
  1674. } else {
  1675. evaluationType.value = type;
  1676. }
  1677. let params = {
  1678. productId: productId.value,
  1679. current: evaluationCurrent.value,
  1680. pageSize: evaluationPageSize.value,
  1681. type: evaluationType.value,
  1682. };
  1683. get("v3/goods/front/goods/comment", params).then((res) => {
  1684. if (res.state == 200) {
  1685. let result = res.data;
  1686. goodsCommentsInfo.data = result;
  1687. score.value = Number(goodsCommentsInfo.data.avgScore);
  1688. goodsCommentsInfo.data &&
  1689. goodsCommentsInfo.data.list &&
  1690. goodsCommentsInfo.data.list.length > 0 &&
  1691. goodsCommentsInfo.data.list.map((commentsItem) => {
  1692. commentsItem.memberName =
  1693. commentsItem.memberName.slice(0, 1) +
  1694. "***" +
  1695. commentsItem.memberName.slice(
  1696. commentsItem.memberName.length - 1,
  1697. commentsItem.memberName.length
  1698. );
  1699. });
  1700. } else {
  1701. ElMessage.error(res.msg);
  1702. }
  1703. });
  1704. };
  1705. //评价列表上一页
  1706. const handlePrevCilickChange = () => {
  1707. if (evaluationCurrent.value == 1) {
  1708. evaluationCurrent.value = 1;
  1709. } else {
  1710. evaluationCurrent.value--;
  1711. }
  1712. getEvaluation(evaluationType.value);
  1713. };
  1714. //评价列表下一页
  1715. const handleNextCilickChange = () => {
  1716. if (evaluationCurrent.value >= goodsCommentsInfo.data.list.length) {
  1717. evaluationCurrent.value = goodsCommentsInfo.data.list.length;
  1718. } else {
  1719. evaluationCurrent.value++;
  1720. }
  1721. getEvaluation(evaluationType.value);
  1722. };
  1723. //评价列表当前选择页
  1724. const handleCurrentChange = (val) => {
  1725. evaluationCurrent.value = val;
  1726. getEvaluation(evaluationType.value);
  1727. };
  1728. //获取热销店铺推荐
  1729. const getSalestore = () => {
  1730. let params = {
  1731. storeId: goodsDetail.data.storeInf.storeId,
  1732. sort: 7,
  1733. pageSize: salesPageSize.value,
  1734. current: salesCurrent.value,
  1735. };
  1736. get("v3/goods/front/goods/goodsList", params).then((res) => {
  1737. if (res.state == 200) {
  1738. let result = res.data;
  1739. recommendeData.data = result;
  1740. recommendedList.data = result.list;
  1741. recommendedList.data.map(
  1742. (item) => (item.goodsPrice = new Number(item.goodsPrice).toFixed(2))
  1743. );
  1744. } else {
  1745. ElMessage.error(res.msg);
  1746. }
  1747. });
  1748. };
  1749. //店铺热销列表上一页
  1750. const handlePrevCilickChangeSales = () => {
  1751. if (evaluationCurrent.value == 1) {
  1752. salesCurrent.value = 1;
  1753. } else {
  1754. salesCurrent.value--;
  1755. }
  1756. getSalestore();
  1757. };
  1758. //店铺热销列表下一页
  1759. const handleNextCilickChangeSales = () => {
  1760. if (salesCurrent.value >= goodsCommentsInfo.data.list.length) {
  1761. salesCurrent.value = goodsCommentsInfo.data.list.length;
  1762. } else {
  1763. salesCurrent.value++;
  1764. }
  1765. getSalestore();
  1766. };
  1767. //店铺热销列表当前选择页
  1768. const handleCurrentChangeSales = (val) => {
  1769. salesCurrent.value = val;
  1770. getSalestore();
  1771. };
  1772. //关注店铺及取消关注
  1773. const headercat = ref(null)
  1774. const focusStore = () => {
  1775. if (filtersStore.getLoginFlag) {
  1776. //已登录
  1777. let params = {
  1778. storeIds: goodsDetail.data.storeInf.storeId,
  1779. isCollect: !goodsDetail.data.storeInf.isFollowStore,
  1780. };
  1781. post("v3/member/front/followStore/edit", params).then((res) => {
  1782. if (res.state == 200) {
  1783. goodsDetail.data.storeInf.isFollowStore =!goodsDetail.data.storeInf.isFollowStore;
  1784. proxy.refs.headercat.setfollowStore(goodsDetail.data.storeInf.isFollowStore? "true" : "false")
  1785. if (goodsDetail.data.storeInf.isFollowStore) {
  1786. sldStatEvent({
  1787. behaviorType: "fol",
  1788. storeId: goodsDetail.data.storeInf.storeId,
  1789. });
  1790. }
  1791. }
  1792. });
  1793. } else {
  1794. //未登录提示登录
  1795. loginModalVisibleFlag.value = true;
  1796. }
  1797. };
  1798. //商品收藏及取消收藏
  1799. const collectGoods = () => {
  1800. if (filtersStore.getLoginFlag) {
  1801. //已登录
  1802. let params = {
  1803. productIds: productId.value,
  1804. isCollect: !goodsDetail.data.followGoods,
  1805. };
  1806. post("v3/member/front/followProduct/edit", params).then((res) => {
  1807. if (res.state == 200) {
  1808. goodsDetail.data.followGoods = !goodsDetail.data.followGoods;
  1809. if (goodsDetail.data.followGoods) {
  1810. sldStatEvent({
  1811. behaviorType: "fav",
  1812. goodsId: goodsDetail.data.goodsId,
  1813. storeId: goodsDetail.data.storeInf.storeId,
  1814. });
  1815. }
  1816. } else {
  1817. ElMessage.error(res.msg);
  1818. }
  1819. });
  1820. } else {
  1821. //未登录提示登录
  1822. loginModalVisibleFlag.value = true;
  1823. }
  1824. };
  1825. //点击查看全部查看全部的商品规格参数
  1826. const openParameter = () => {
  1827. parameterShow.value = !parameterShow.value;
  1828. };
  1829. //选择商品主图
  1830. const selectDefaultImage = (goodsImgItem, goodsImgIndex) => {
  1831. defaultImage.value = goodsImgItem;
  1832. currentDefaultImage.value = goodsImgIndex;
  1833. };
  1834. //切换商品主图
  1835. const switchDefaultImage = (type) => {
  1836. let defaultImagelength = goodsDetail.data.defaultProduct.goodsPics.length;
  1837. if (type == "left") {
  1838. currentDefaultImage.value--;
  1839. if (currentDefaultImage.value <= 0) {
  1840. currentDefaultImage.value = 0;
  1841. }
  1842. defaultImage.value =
  1843. goodsDetail.data.defaultProduct.goodsPics[currentDefaultImage.value];
  1844. } else {
  1845. currentDefaultImage.value++;
  1846. if (currentDefaultImage.value >= defaultImagelength) {
  1847. currentDefaultImage.value = defaultImagelength - 1;
  1848. }
  1849. defaultImage.value =
  1850. goodsDetail.data.defaultProduct.goodsPics[currentDefaultImage.value];
  1851. }
  1852. goodsPictureListsLeft();
  1853. };
  1854. //图片列表的left移动的距离
  1855. const goodsPictureListsLeft = () => {
  1856. //获取 goods_picture_list 的元素
  1857. let goodsPictureLists = goodsPictureList.value;
  1858. //列表默认显示5张图片
  1859. if (
  1860. goodsDetail.data.defaultProduct.goodsPics.length > 5 &&
  1861. currentDefaultImage.value >= 0
  1862. ) {
  1863. /* 分析找规律:
  1864. 如果有8张图片,点击右键 最大可以向左移动的距离为 8 - 5既3张图,left移动的距离为 3 * - 66px; currentDefaultImage.value从0开始的
  1865. 其中:66px为每一个元素需要每次移动的距离 为图片的宽度与图片之间的间距的和
  1866. currentDefaultImage.value == 4;goodsPictureLists.style.left = 0;
  1867. currentDefaultImage.value == 5;goodsPictureLists.style.left = (5-4) (1) * -66px;
  1868. currentDefaultImage.value == 6;goodsPictureLists.style.left = (5-2) (2) * -66px;
  1869. currentDefaultImage.value == 7;goodsPictureLists.style.left = (5-3) (2) * -66px;
  1870. 点击左键,最大可以向右移动的距离为 8 - 5 即3张
  1871. currentDefaultImage.value == 0;goodsPictureLists.style.left = 0;
  1872. currentDefaultImage.value == 1; goodsPictureLists.style.left = -66px;
  1873. currentDefaultImage.value == 2; goodsPictureLists.style.left = 2 * -66px;
  1874. currentDefaultImage.value == 3; goodsPictureLists.style.left = 3 * -66px;
  1875. */
  1876. if (currentDefaultImage.value > 4) {
  1877. goodsPictureLists.style.left =
  1878. (currentDefaultImage.value - 4) * -66 + "px";
  1879. }
  1880. if (
  1881. currentDefaultImage.value <
  1882. goodsDetail.data.defaultProduct.goodsPics.length - 4
  1883. ) {
  1884. goodsPictureLists.style.left = currentDefaultImage.value * -66 + "px";
  1885. }
  1886. }
  1887. };
  1888. //获取元素距离父元素的顶部及左边的距离
  1889. const offset = (el) => {
  1890. if (process.client) {
  1891. let top = el.offsetTop;
  1892. let left = el.offsetLeft;
  1893. if (el.offsetParent) {
  1894. el = el.offsetParent;
  1895. top += el.offsetTop;
  1896. left += el.offsetLeft;
  1897. }
  1898. return {
  1899. left: left,
  1900. top: top,
  1901. };
  1902. }
  1903. };
  1904. if (process.client) {
  1905. nextTick(() => {
  1906. containerTop.value = offset(container.value).top;
  1907. window.addEventListener("scroll", scrollHandle); //绑定页面滚动事件
  1908. window.addEventListener("click", () => {
  1909. addrDialogVisible.value = false;
  1910. });
  1911. });
  1912. }
  1913. //鼠标移动
  1914. const move = (e) => {
  1915. if (currentDefaultImage.value == -1) {
  1916. return;
  1917. }
  1918. //主图父元素的信息 宽,高
  1919. let previewsBox = previewBox.value;
  1920. let previewBoxWidth = previewsBox.offsetWidth;
  1921. let previewBoxHeight = previewsBox.offsetHeight;
  1922. //主图父元素距离顶部的距离
  1923. let previewsBoxLeft = offset(previewsBox).left;
  1924. let previewsBoxTop = offset(previewsBox).top;
  1925. // 遮罩盒子的信息宽,高
  1926. let masksBox = maskBox.value;
  1927. let maskBoxWidth = masksBox.offsetWidth;
  1928. let maskBoxHeight = masksBox.offsetHeight;
  1929. //鼠标距离屏幕距离
  1930. let moveX = e.clientX;
  1931. let moveY = e.clientY;
  1932. //获取左侧大图父元素的信息
  1933. let zoomsBox = zoomBox.value;
  1934. let zoomBoxWidth = zoomsBox.offsetWidth;
  1935. let zoomBoxHeight = zoomsBox.offsetHeight;
  1936. // 获取大图元素的信息宽,高
  1937. let pictureBigBox = pictureBig.value;
  1938. let pictureBigWidth = pictureBigBox.offsetWidth;
  1939. let pictureBigHeight = pictureBigBox.offsetHeight;
  1940. //获取滚动条的高度
  1941. let scroll = document.documentElement.scrollTop || document.body.scrollTop;
  1942. //主图距离父元素的left及top值
  1943. let left = moveX - previewsBoxLeft - maskBoxWidth / 2;
  1944. let top;
  1945. if (scroll > 0) {
  1946. top = moveY - previewsBoxTop + scroll - maskBoxHeight / 2;
  1947. } else {
  1948. top = moveY - previewsBoxTop - maskBoxHeight / 2;
  1949. }
  1950. //移动限制最大宽度,及最大高度
  1951. let maxWidth = previewBoxWidth - maskBoxWidth;
  1952. let maxHeight = previewBoxHeight - maskBoxHeight;
  1953. left = left < 0 ? 0 : left > maxWidth ? maxWidth : left;
  1954. top = top < 0 ? 0 : top > maxHeight ? maxHeight : top;
  1955. //比列
  1956. let parcentX = left / maxWidth;
  1957. let parcentY = top / maxHeight;
  1958. //遮罩层的定位值
  1959. maskBox.value.style.left = left + "px";
  1960. maskBox.value.style.top = top + "px";
  1961. //大图元素的定位值
  1962. pictureBig.value.style.left =
  1963. parcentX * (zoomBoxWidth - pictureBigWidth) + "px";
  1964. pictureBig.value.style.top =
  1965. parcentY * (zoomBoxHeight - pictureBigHeight) + "px";
  1966. pictureBig.value.style.width =
  1967. (previewBoxWidth / maskBoxWidth) * zoomBoxWidth + "px";
  1968. pictureBig.value.style.height =
  1969. (previewBoxHeight / maskBoxHeight) * zoomBoxHeight + "px";
  1970. };
  1971. //鼠标移出
  1972. const out = () => {
  1973. maskShow.value = false;
  1974. };
  1975. //鼠标移入
  1976. const enter = () => {
  1977. if (currentDefaultImage.value == -1) {
  1978. return;
  1979. }
  1980. addrDialogVisible.value = false;
  1981. maskShow.value = true;
  1982. };
  1983. // 评论区查看图片
  1984. const showImg = (index, img) => {
  1985. imgSource.value = img;
  1986. imgIndex.value = index;
  1987. imgVisible.value = true;
  1988. };
  1989. //分享
  1990. const share = (type) => {
  1991. let title = goodsDetail.data.goodsName; //需要分享的标题,这里取商品名字
  1992. let url = goodsDetail.data.shareLink; //分享的地址,用户点击可以进入到该商品
  1993. let content = goodsDetail.data.goodsBrief; //自定义内容,这里取商品广告词
  1994. let targetUrl = ""; //跳转的url地址
  1995. if (type == "weixin") {
  1996. wxShareCode.value = true;
  1997. //微信
  1998. let canvas = qrcanvas({
  1999. data: url, //二维码内容
  2000. size: 100,
  2001. colorDark: "red",
  2002. });
  2003. let share_wx_qrcode = document.getElementById("share_wx_qrcode");
  2004. if (
  2005. wxShareCode.value &&
  2006. share_wx_qrcode != null &&
  2007. share_wx_qrcode != undefined
  2008. ) {
  2009. document.getElementById("share_wx_qrcode").innerHTML = "";
  2010. document.getElementById("share_wx_qrcode").appendChild(canvas);
  2011. }
  2012. } else if (type == "qzone") {
  2013. wxShareCode.value = false;
  2014. //QQ空间
  2015. targetUrl =
  2016. "https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?title=" +
  2017. encodeURIComponent(title) +
  2018. "&desc=" +
  2019. encodeURIComponent(content) +
  2020. "&summary=" +
  2021. encodeURIComponent(content) +
  2022. "&url=" +
  2023. encodeURIComponent(url);
  2024. window.open(targetUrl, "_blank");
  2025. } else if (type == "sina") {
  2026. wxShareCode.value = false;
  2027. //新浪微博
  2028. targetUrl =
  2029. "https://service.weibo.com/share/share.php?title=" +
  2030. encodeURIComponent(content + "「" + title + "」" + " 点这里" + url);
  2031. window.open(targetUrl, "_blank");
  2032. }
  2033. };
  2034. const refreshInfo = () => {
  2035. history.go(0);
  2036. };
  2037. const updateFllow = (e) => {
  2038. goodsDetail.data.storeInf.isFollowStore = e.state == "true" ? true : false;
  2039. };
  2040. //暴露的变量及方法
  2041. </script>
  2042. <style lang="scss">
  2043. @import "@/assets/style/base.scss";
  2044. @import "@/assets/style/theme.scss";
  2045. @import "@/assets/style/iconfont.css";
  2046. @import "@/assets/style/goodsDetail.scss";
  2047. .popular_list_empty {
  2048. height: 95px;
  2049. font-size: 14px;
  2050. /*font-family: Microsoft YaHei;*/
  2051. font-weight: 400;
  2052. color: #666666;
  2053. }
  2054. .imageBorder {
  2055. border: 1px solid #eee;
  2056. }
  2057. .goods_picture_big {
  2058. border: 1px solid #eee;
  2059. }
  2060. .el-radio__inner:hover {
  2061. border-color: $colorMain;
  2062. }
  2063. .el-radio__input.is-checked .el-radio__inner {
  2064. border-color: $colorMain;
  2065. background: $colorMain;
  2066. }
  2067. .el-radio__input.is-checked + .el-radio__label {
  2068. color: $colorMain;
  2069. }
  2070. .el-radio {
  2071. margin-bottom: 10px;
  2072. display: flex;
  2073. align-items: flex-start;
  2074. white-space: unset;
  2075. margin-right: unset;
  2076. }
  2077. .el-radio-button__inner,
  2078. .el-radio-group {
  2079. /* display: block; */
  2080. line-height: 1;
  2081. vertical-align: middle;
  2082. }
  2083. .el-radio__label {
  2084. font-size: 13px;
  2085. width: 320px;
  2086. overflow: hidden;
  2087. text-overflow: ellipsis;
  2088. display: -webkit-box;
  2089. -webkit-line-clamp: 2;
  2090. -webkit-box-orient: vertical;
  2091. word-break: break-all;
  2092. line-height: 22px;
  2093. margin-top: -5px;
  2094. }
  2095. .evaluationes {
  2096. color: #3b4 !important;
  2097. }
  2098. .sld_goods_detail .goods_htmls .ql-video {
  2099. width: 525px;
  2100. height: 315px;
  2101. }
  2102. .sld_goods_detail .goods_htmls a {
  2103. display: inline-block;
  2104. margin: 5px auto;
  2105. color: #0000ff;
  2106. text-decoration: underline;
  2107. }
  2108. .sld_goods_detail .goods_htmls table {
  2109. border-collapse: collapse;
  2110. padding: 0;
  2111. }
  2112. .sld_goods_detail .goods_htmls td,
  2113. .sld_goods_detail .goods_htmls th {
  2114. border: 1px solid #ddd;
  2115. padding: 5px 10px;
  2116. }
  2117. .sld_goods_detail .goods_htmls ol li,
  2118. .sld_goods_detail .goods_htmls ul li {
  2119. list-style: unset;
  2120. }
  2121. .sld_goods_detail {
  2122. .summary {
  2123. .coupon {
  2124. .el-dialog__header {
  2125. padding-top: 18px;
  2126. padding-bottom: 18px;
  2127. .el-dialog__title {
  2128. color: #333333;
  2129. /*font-family: Microsoft YaHei;*/
  2130. font-weight: bold;
  2131. }
  2132. .el-dialog__close {
  2133. color: #333333;
  2134. font-size: 20px;
  2135. }
  2136. }
  2137. .el-dialog__body {
  2138. background: #f8f8f8;
  2139. }
  2140. }
  2141. }
  2142. }
  2143. .summary-info{
  2144. }
  2145. .mt-20{
  2146. margin-top: 20px;
  2147. }
  2148. .summary_htmls img{
  2149. width: 100%;
  2150. }
  2151. </style>